package com.hsxxherp.simplify.framework.utils;

import com.hsxxherp.simplify.framework.algorithm.SM3Utils;

import java.security.SecureRandom;

public class PasswordEncryptionUtils {

    private static final int ITERATIONS = 1000;
    private static final int SALT_LENGTH = 16;
    private static final byte CURRENT_VERSION = 1;
    private static final String ALGORITHM_IDENTIFIER = "SM3";

    private PasswordEncryptionUtils() {
        // 私有构造函数，防止实例化
    }

    /**
     * 生成盐
     * @return 盐的字节数组
     */
    private static byte[] generateSalt() {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[SALT_LENGTH];
        random.nextBytes(salt);
        return salt;
    }

    /**
     * 对明文密码进行加密，将算法标识符、版本号、salt 包含在加密结果中
     * @param password 明文密码
     * @return 包含算法标识符、版本号、salt 和加密后密码的十六进制字符串
     */
    public static String hashPassword(String password) {
        byte[] salt = generateSalt();
        byte[] hash = hashWithSaltAndIterations(password.getBytes(), salt, ITERATIONS);

        byte[] algorithmIdentifierBytes = ALGORITHM_IDENTIFIER.getBytes();

        byte[] combined = new byte[algorithmIdentifierBytes.length + 1 + SALT_LENGTH + hash.length];
        System.arraycopy(algorithmIdentifierBytes, 0, combined, 0, algorithmIdentifierBytes.length);
        combined[algorithmIdentifierBytes.length] = CURRENT_VERSION;
        System.arraycopy(salt, 0, combined, algorithmIdentifierBytes.length + 1, SALT_LENGTH);
        System.arraycopy(hash, 0, combined, algorithmIdentifierBytes.length + 1 + SALT_LENGTH, hash.length);

        return bytesToHex(combined);
    }

    /**
     * 多次迭代使用 SM3 算法进行哈希
     * @param password 明文密码的字节数组
     * @param salt 盐的字节数组
     * @param iterations 迭代次数
     * @return 加密后的字节数组
     */
    private static byte[] hashWithSaltAndIterations(byte[] password, byte[] salt, int iterations) {
        byte[] input = new byte[password.length + salt.length];
        System.arraycopy(salt, 0, input, 0, salt.length);
        System.arraycopy(password, 0, input, salt.length, password.length);

        byte[] hash = input;
        for (int i = 0; i < iterations; i++) {
            hash = hexToBytes(SM3Utils.hash(hash));
        }
        return hash;
    }

    /**
     * 验证明文密码和包含算法标识符、版本号、salt 的加密结果是否匹配
     * @param password 明文密码
     * @param storedHash 包含算法标识符、版本号、salt 和加密后密码的十六进制字符串
     * @return 如果匹配返回 true，否则返回 false
     */
    public static boolean checkPassword(String password, String storedHash) {
        byte[] combined = hexToBytes(storedHash);

        String storedAlgorithmIdentifier = new String(combined, 0, ALGORITHM_IDENTIFIER.length());
        if (!ALGORITHM_IDENTIFIER.equals(storedAlgorithmIdentifier)) {
            return false;
        }

        byte version = combined[ALGORITHM_IDENTIFIER.length()];
        if (version == 1) {
            byte[] salt = new byte[SALT_LENGTH];
            System.arraycopy(combined, ALGORITHM_IDENTIFIER.length() + 1, salt, 0, SALT_LENGTH);

            byte[] storedHashBytes = new byte[combined.length - ALGORITHM_IDENTIFIER.length() - 1 - SALT_LENGTH];
            System.arraycopy(combined, ALGORITHM_IDENTIFIER.length() + 1 + SALT_LENGTH, storedHashBytes, 0, storedHashBytes.length);

            byte[] newHash = hashWithSaltAndIterations(password.getBytes(), salt, ITERATIONS);

            return java.util.Arrays.equals(newHash, storedHashBytes);
        }
        return false;
    }

    /**
     * 将字节数组转换为十六进制字符串
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }

    /**
     * 将十六进制字符串转换为字节数组
     * @param hex 十六进制字符串
     * @return 字节数组
     */
    private static byte[] hexToBytes(String hex) {
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }
}